package com.dny.asmtop.op;

import com.dny.asmtop.ASMMethodUtils;
import com.dny.asmtop.Command;
import com.dny.asmtop.MethodContext;
import jdk.internal.org.objectweb.asm.Type;
import jdk.internal.org.objectweb.asm.commons.GeneratorAdapter;

import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import static java.lang.String.format;
import static jdk.internal.org.objectweb.asm.Type.getType;
import static jdk.internal.org.objectweb.asm.commons.Method.getMethod;

/**
 * Created by jlutt on 2018-01-16.
 * new对象
 * @author jlutt
 */
public class CommandConstructor implements Command {

  private final Class<?> type;
  private final List<Command> fields;

  public CommandConstructor(Class<?> type, Command... fields) {
    this.type = type;
    this.fields = Arrays.asList(fields);
  }

  public CommandConstructor(Class<?> type, List<Command> fields) {
    this.type = type;
    this.fields = fields;
  }

  @Override
  public Type type(MethodContext context) {
    return getType(type);
  }

  @Override
  public Type generator(MethodContext context) {
    GeneratorAdapter g = context.getGeneratorAdapter();
    Class<?>[] fieldTypes = new Class<?>[this.fields.size()];
    Command[] fieldVars = new Command[this.fields.size()];
    for (int i = 0; i < this.fields.size(); i++) {
      Command field = this.fields.get(i);
      Type fieldType = field.type(context);
      fieldTypes[i] = ASMMethodUtils.getJavaType(context.getClassLoader(), fieldType);
      fieldVars[i] = field;
    }
    try {
      Constructor<?> constructor = type.getConstructor(fieldTypes);
      g.newInstance(getType(type));
      g.dup();
      for (Command fieldVar : fieldVars) {
        fieldVar.generator(context);
      }
      g.invokeConstructor(getType(type), getMethod(constructor));
      return getType(type);
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(format("No constructor %s.<init>(%s). %s",
          type.getName(),
          (fieldTypes.length != 0 ? ASMMethodUtils.argsToString(fieldTypes) : ""),
          ASMMethodUtils.exceptionInGeneratedClass(context)));
    }
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    CommandConstructor that = (CommandConstructor) o;
    return Objects.equals(type, that.type) &&
        Objects.equals(fields, that.fields);
  }

  @Override
  public int hashCode() {
    return Objects.hash(type, fields);
  }
}
